/*******************************************************************************
 * Copyright (c) 2009-2011 ETCORE Labs. All rights reserved. 
 * 
 * This software is the confidential and proprietary information of ETCORE Labs.
 * You shall not disclose such confidential information and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with ETCORE Labs. 
 * 
 * Support can be obtained from support forums at:  http://www.etcore.com/
 * /
 *******************************************************************************/
package etcore.webapp.grouptalk.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * FileUtil is a utility class with operations of copying,deleting files
 * 
 * @author ETCORE GroupTalk Developers
 * 
 */
public class FileUtil {
	/**
	 * Copy files from source directory to target directory
	 * 
	 * @param sourceDirectory
	 *            File instance of source directory
	 * @param targetDirectory
	 *            File instance of target directory
	 */
	public static void copyDir(File sourceDirectory, File targetDirectory) {

		if (sourceDirectory != null && sourceDirectory.isDirectory()) {
			if (!targetDirectory.exists())
				targetDirectory.mkdirs();
			File[] entries = sourceDirectory.listFiles();

			int sz = entries.length;
			for (int i = 0; i < sz; i++) {
				if (entries[i].isDirectory()) {
					copyDir(entries[i],
							new File(targetDirectory.getAbsoluteFile()
									+ Symbols.SLASH + entries[i].getName()));

				} else {
					copyFile(entries[i],
							new File(targetDirectory.getAbsolutePath()
									+ Symbols.SLASH + entries[i].getName()));

				}
			}
		}

	}

	/**
	 * Copy source file into target file
	 * 
	 * @param sourceFile
	 *            File instance of source file
	 * @param targetFile
	 *            File instance of target file
	 */
	public static void copyFile(File sourceFile, File targetFile) {
		if (targetFile != null && targetFile.exists()) {

			return;
		}
		try {

			FileInputStream input = new FileInputStream(sourceFile);
			BufferedInputStream inBuff = new BufferedInputStream(input);

			FileOutputStream output = new FileOutputStream(targetFile);
			BufferedOutputStream outBuff = new BufferedOutputStream(output);

			int len, bufferSize = 1024 * 5;
			byte[] b = new byte[bufferSize];

			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}

			outBuff.flush();

			inBuff.close();
			outBuff.close();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Copy source file into target file
	 * 
	 * @param sourceFile
	 *            InputStream instance of source file
	 * @param targetFile
	 *            File instance of target file
	 */
	public static void copyFile(InputStream sourceFile, File targetFile) {
		if (targetFile != null && targetFile.exists()) {

			return;
		}
		try {

			InputStream input = sourceFile;
			BufferedInputStream inBuff = new BufferedInputStream(input);

			FileOutputStream output = new FileOutputStream(targetFile);
			BufferedOutputStream outBuff = new BufferedOutputStream(output);

			int len, bufferSize = 1024 * 5;
			byte[] b = new byte[bufferSize];

			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}

			outBuff.flush();

			inBuff.close();
			outBuff.close();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Copy source file or directory into target path
	 * 
	 * @param sourcePath
	 *            path of source file or directory
	 * @param targetPath
	 *            path of target file or directory
	 */
	public static void copyFileOrDir(String sourcePath, String targetPath) {

		if (sourcePath == null || targetPath == null)
			return;

		File source = null;
		String filteredSource = sourcePath.replace(Symbols.DOUBLE_SLASH,
				Symbols.SLASH);
		if (filteredSource.startsWith("jar:file:")) {
			String prefix = filteredSource.substring(0,
					sourcePath.indexOf("!/") + 2);
			String jarPath = filteredSource
					.replace("jar:file:/", Symbols.BLANK);
			if (jarPath.indexOf(Symbols.EXCLAMATION_MARK) == -1)
				return;
			String entryName = jarPath.substring(jarPath.indexOf("!/") + 2)
					.replace(Symbols.DOUBLE_SLASH, Symbols.SLASH);
			if (entryName.startsWith(Symbols.SLASH))
				entryName = entryName.substring(entryName
						.indexOf(Symbols.SLASH) + 1);
			jarPath = jarPath.substring(0,
					jarPath.indexOf(Symbols.EXCLAMATION_MARK));
			try {
				JarFile jarFile = new JarFile(jarPath);
				JarEntry entry = jarFile.getJarEntry(entryName);
				int isD = -1;
				if (entry == null) {
					if (entryName.endsWith(Symbols.SLASH))
						entryName = entryName.substring(0,
								entryName.length() - 1);
					if (entryName.lastIndexOf(Symbols.SLASH) != -1) {
						String testName = entryName.substring(entryName
								.lastIndexOf(Symbols.SLASH) + 1);
						if (testName != null
								&& testName.indexOf(Symbols.DOT) == -1)
							isD = 1;

					}
				}

				if (isD == 1) {
					Set<String> subFilePathSet = getFullPathOfSubFiles(jarFile,
							entryName, prefix);

					if (subFilePathSet != null) {
						for (Iterator<String> iter = subFilePathSet.iterator(); iter
								.hasNext();) {
							String path = iter.next();

							copyJarFile(path, filteredSource, targetPath);
						}
					}
				} else if (entry != null) {

					InputStream in = ConfigUtil.getInputStream(filteredSource);
					if (in == null)
						return;
					copyFile(in, new File(targetPath));

				}

			} catch (Exception e) {
				e.printStackTrace();
			}

		} else {
			source = new File(sourcePath);

			File target = new File(targetPath);
			if (source != null && source.isFile())
				copyFile(source, target);
			else
				copyDir(source, target);
		}
	}

	/**
	 * Copy a jar file into target path
	 * 
	 * @param fullPath
	 *            path of source file
	 * @param filterPath
	 *            a string to be deleted in target file path
	 * @param targetPath
	 *            target path prefix
	 */
	public static void copyJarFile(String fullPath, String filterPath,
			String targetPath) {
		if (fullPath == null || filterPath == null || targetPath == null)
			return;

		String tmpPath = fullPath.replace(filterPath, Symbols.BLANK);

		String targetFile = targetPath + Symbols.SLASH + tmpPath;

		String targetDir = targetFile.substring(0,
				targetFile.lastIndexOf(Symbols.SLASH));
		File targetDirFile = new File(targetDir);
		if (!targetDirFile.exists())
			targetDirFile.mkdirs();

		InputStream in = ConfigUtil.getInputStream(fullPath);
		if (in == null)
			return;
		copyFile(in, new File(targetFile));

	}

	/**
	 * Delete a directory
	 * 
	 * @param dir
	 *            a directory File
	 */
	public static void deleteDir(File dir) {
		if (dir != null && dir.isDirectory()) {
			File[] entries = dir.listFiles();

			int sz = entries.length;
			for (int i = 0; i < sz; i++) {
				if (entries[i].isDirectory()) {
					deleteDir(entries[i]);
				} else {
					entries[i].delete();
				}
			}
			dir.delete();
		}

	}

	/**
	 * Delete a file or directory
	 * 
	 * @param filePath
	 *            path of file or directory
	 */
	public static void deleteFileOrDir(String filePath) {
		File file = new File(filePath);
		if (file != null) {
			if (file.isFile())
				file.delete();
			else
				deleteDir(file);
		}

	}

	/**
	 * Get sub files' paths of a jar file
	 * 
	 * @param jarFile
	 *            JarFile instance
	 * @param entryName
	 *            entry name
	 * @param prefix
	 *            path prefix
	 * @return
	 */
	public static Set<String> getFullPathOfSubFiles(JarFile jarFile,
			String entryName, String prefix) {
		if (jarFile == null || entryName == null || prefix == null)
			return null;
		Enumeration<JarEntry> jarEntries = jarFile.entries();
		Set<String> resultSet = new HashSet<String>();
		while (jarEntries.hasMoreElements()) {
			JarEntry entry = (JarEntry) jarEntries.nextElement();
			String name = entry.getName();
			if (name.indexOf(entryName) != -1 && !entry.isDirectory()) {
				String path = prefix + name;
				resultSet.add(path);
			}
		}

		return resultSet;
	}

	/**
	 * Override a directory
	 * 
	 * @param source
	 *            File instance of source directory
	 * @param target
	 *            File instance of target directory
	 */
	public static void overRiddenDir(File source, File target) {

		if (source != null && source.isDirectory()) {
			if (target != null && !target.exists()) {
				target.mkdirs();
			}

			File[] entries = source.listFiles();

			int sz = entries.length;
			for (int i = 0; i < sz; i++) {
				if (entries[i].isDirectory()) {
					overRiddenDir(entries[i], new File(target.getAbsoluteFile()
							+ Symbols.SLASH + entries[i].getName()));

				} else {
					overRiddenFile(entries[i],
							new File(target.getAbsolutePath() + Symbols.SLASH
									+ entries[i].getName()));

				}
			}
		}

	}

	/**
	 * Override a file
	 * 
	 * @param sourceFile
	 *            File instance of source file
	 * @param targetFile
	 *            File instance of target file
	 */
	public static void overRiddenFile(File sourceFile, File targetFile) {

		try {
			FileInputStream input = new FileInputStream(sourceFile);
			BufferedInputStream inBuff = new BufferedInputStream(input);

			FileOutputStream output = new FileOutputStream(targetFile);
			BufferedOutputStream outBuff = new BufferedOutputStream(output);

			int len, bufferSize = 1024 * 5;
			byte[] b = new byte[bufferSize];

			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}

			outBuff.flush();

			inBuff.close();
			outBuff.close();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Override a file
	 * 
	 * @param sourceFile
	 *            InputStream instance of source file
	 * @param targetFile
	 *            target file
	 */
	public static void overRiddenFile(InputStream sourceFile, File targetFile) {

		try {

			InputStream input = sourceFile;
			BufferedInputStream inBuff = new BufferedInputStream(input);

			FileOutputStream output = new FileOutputStream(targetFile);
			BufferedOutputStream outBuff = new BufferedOutputStream(output);

			int len, bufferSize = 1024 * 5;
			byte[] b = new byte[bufferSize];

			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}

			outBuff.flush();

			inBuff.close();
			outBuff.close();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Override a file
	 * 
	 * @param sourceFile
	 *            source file path
	 * @param targetFile
	 *            File instance of target file
	 */
	public static void overRiddenFile(String sourceFile, File targetFile) {

		try {
			InputStream input = ConfigUtil.getInputStream(sourceFile);
			BufferedInputStream inBuff = new BufferedInputStream(input);

			FileOutputStream output = new FileOutputStream(targetFile);
			BufferedOutputStream outBuff = new BufferedOutputStream(output);

			int len, bufferSize = 1024 * 5;
			byte[] b = new byte[bufferSize];

			while ((len = inBuff.read(b)) != -1) {
				outBuff.write(b, 0, len);
			}

			outBuff.flush();

			inBuff.close();
			outBuff.close();
			output.close();
			input.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Override a file or directory
	 * 
	 * @param sourcePath
	 *            path of source file or directory
	 * @param targetPath
	 *            path of target file or directory
	 */
	public static void overRiddenFileOrDir(String sourcePath, String targetPath) {
		File source = null;
		if (sourcePath.startsWith("jar:file:")) {
			String filteredSource = sourcePath.replace(Symbols.DOUBLE_SLASH,
					Symbols.SLASH);
			String prefix = filteredSource.substring(0,
					sourcePath.indexOf("!/") + 2);
			String jarPath = filteredSource
					.replace("jar:file:/", Symbols.BLANK);
			if (jarPath.indexOf(Symbols.EXCLAMATION_MARK) == -1)
				return;
			String entryName = jarPath.substring(jarPath.indexOf("!/") + 2)
					.replace(Symbols.DOUBLE_SLASH, Symbols.SLASH);
			if (entryName.startsWith(Symbols.SLASH))
				entryName = entryName.substring(entryName
						.indexOf(Symbols.SLASH) + 1);
			jarPath = jarPath.substring(0,
					jarPath.indexOf(Symbols.EXCLAMATION_MARK));
			try {
				JarFile jarFile = new JarFile(jarPath);
				JarEntry entry = jarFile.getJarEntry(entryName);
				int isDidrectory = -1;
				if (entry == null) {
					if (entryName.endsWith(Symbols.SLASH))
						entryName = entryName.substring(0,
								entryName.length() - 1);
					if (entryName.lastIndexOf(Symbols.SLASH) != -1) {
						String testName = entryName.substring(entryName
								.lastIndexOf(Symbols.SLASH) + 1);
						if (testName != null
								&& testName.indexOf(Symbols.DOT) == -1)
							isDidrectory = 1;

					}
				}

				if (isDidrectory == 1) {
					Set<String> subFilePathSet = getFullPathOfSubFiles(jarFile,
							entryName, prefix);

					if (subFilePathSet != null) {
						for (Iterator<String> pathIterator = subFilePathSet
								.iterator(); pathIterator.hasNext();) {
							String path = pathIterator.next();

							overRiddenJarFile(path, filteredSource, targetPath);
						}
					}
				} else if (entry != null) {
					InputStream in = ConfigUtil.getInputStream(filteredSource);
					if (in == null)
						return;
					overRiddenFile(in, new File(targetPath));

				}

			} catch (Exception e) {
				e.printStackTrace();
			}

		} else {
			source = new File(sourcePath);

			File target = new File(targetPath);
			if (source != null && source.isFile())
				overRiddenFile(source, target);
			else
				overRiddenDir(source, target);
		}
	}

	/**
	 * 
	 * @param fullPath
	 *            path of source file
	 * @param filterPath
	 *            string to be deleted in target path
	 * @param targetPath
	 *            prefix of target path
	 */
	public static void overRiddenJarFile(String fullPath, String filterPath,
			String targetPath) {
		if (fullPath == null || filterPath == null || targetPath == null)
			return;
		String tmpPath = fullPath.replace(filterPath, Symbols.BLANK);
		String targetFile = targetPath + Symbols.SLASH + tmpPath;
		String targetDir = targetFile.substring(0,
				targetFile.lastIndexOf(Symbols.SLASH));
		File targetDirFile = new File(targetDir);
		if (!targetDirFile.exists())
			targetDirFile.mkdirs();

		InputStream in = ConfigUtil.getInputStream(fullPath);
		if (in == null)
			return;
		overRiddenFile(in, new File(targetFile));

	}

}
